#include "PrecompiledHeader.h"
#include "CShell.h"

/*--------------------------------------------------------------------------*/
CShell* g_Shell = NULL;
/*--------------------------------------------------------------------------*/

enum CShellCS
{
    VarMap = 0,
    CmdQueue,
    FuncList = 2 // synchronizowac z cshell.h
};

/*--------------------------------------------------------------------------*/

CShell::CShell() :
	IEngMod("CShell"),
	ICriticalSection(3)
{
	this->m_luaState = luaL_newstate();
	AssertLogC(this->m_luaState == NULL, "Can not initialize LUA Engine!");

	luabind::open(this->m_luaState);

	this->RegisterLuaFunction("logc", sh::logc);
	this->RegisterLuaFunction("logi", sh::logi);
	this->RegisterLuaFunction("logw", sh::logw);
	this->RegisterLuaFunction("loge", sh::loge);

	this->RegisterLuaFunction("exit", sh::exit);
	this->RegisterLuaFunction("loadscript", sh::loadscript);

	this->RegisterLuaFunction("setbool", sh::setbool);
	this->RegisterLuaFunction("setint", sh::setint);
	this->RegisterLuaFunction("setfloat", sh::setfloat);
	this->RegisterLuaFunction("setstring", sh::setstring);

	this->RegisterLuaFunction("getbool", sh::getbool);
	this->RegisterLuaFunction("getint", sh::getint);
	this->RegisterLuaFunction("getfloat", sh::getfloat);
	this->RegisterLuaFunction("getstring", sh::getstring);

	this->RegisterLuaFunction("randfloat", sh::randfloat);
	this->RegisterLuaFunction("randint", sh::randint);

	this->RegisterLuaFunction("benchmark", sh::benchmark);

	this->m_sstream = NULL;
}

/*--------------------------------------------------------------------------*/

void CShell::OnInit()
{
	this->StartTask(0, CalcDesc(true, ECalc::SingleThreadHighPriority));
}

/*--------------------------------------------------------------------------*/

CShell::~CShell()
{
	for (std::map<const std::string, Variable*>::iterator it = this->m_variablesMap.begin(); it != this->m_variablesMap.end(); ++it)
	{
		delete(*it).second;
	}

	lua_close(this->m_luaState);
}

/*--------------------------------------------------------------------------*/

void CShell::Execute(const std::string& cmd)
{
	this->Lock(CmdQueue);
	this->m_commandsQueue.push(cmd);
	this->Unlock(CmdQueue);
}

/*--------------------------------------------------------------------------*/

void CShell::SetSStream(std::stringstream* ss)
{
	this->m_sstream = ss;
}

/*--------------------------------------------------------------------------*/

void CShell::LoadScript(const std::string& path)
{
	AssertLogC(luaL_dofile(this->m_luaState, path.c_str()), "LUA: %s\n", lua_tostring(this->m_luaState, -1));
}

/*--------------------------------------------------------------------------*/

void CShell::GetVars(const std::string& name, std::vector<const std::string>& suggestions)
{
	int namelength = name.length();

	this->Lock(VarMap);

	for (std::map<const std::string, Variable*>::iterator Iter = this->m_variablesMap.begin(); Iter != this->m_variablesMap.end(); ++Iter)
	{
		if ((*Iter).first.substr(0, namelength) == name)
		{
			suggestions.push_back((*Iter).first);
		}
	}

	this->Unlock(VarMap);
}


/*--------------------------------------------------------------------------*/

void CShell::GetCmds(const std::string& name, std::vector<const std::string>& suggestions)
{
	int namelength = name.length();

	this->Lock(FuncList);

	for (std::vector<const std::string>::iterator iter = this->m_functionsVector.begin(); iter != this->m_functionsVector.end(); ++iter)
	{
		if ((*iter).substr(0, namelength) == name)
		{
			suggestions.push_back((*iter));
		}
	}

	this->Unlock(FuncList);
}

/*--------------------------------------------------------------------------*/

bool CShell::ExistsVar(const std::string& name)
{
	this->Lock(VarMap);
	std::map<const std::string, Variable*>::iterator it = this->m_variablesMap.find(name);
	this->Unlock(VarMap);

	return it != this->m_variablesMap.end();
}

/*--------------------------------------------------------------------------*/

Variable* CShell::GetVar(const std::string& name)
{
	this->Lock(VarMap);
	std::map<const std::string, Variable*>::iterator it = this->m_variablesMap.find(name);

	if (it == this->m_variablesMap.end())
	{
		this->Unlock(VarMap);
		return NULL;
	}
	else
	{
		this->Unlock(VarMap);
		return (*it).second;
	}
}

/*--------------------------------------------------------------------------*/

Variable* CShell::AddVar(const std::string& name, int val)
{
	this->Lock(VarMap);
	std::map<const std::string, Variable*>::iterator it = this->m_variablesMap.find(name);

	if (it != this->m_variablesMap.end())
	{
		this->m_variablesMap[name]->Set(val);
		this->Unlock(VarMap);
		return (*it).second;
	}
	else
	{
		Variable* tmp = new Variable(name, val);
		this->m_variablesMap[name] = tmp;
		this->Unlock(VarMap);
		return tmp;
	}
}

/*--------------------------------------------------------------------------*/

Variable* CShell::AddVar(const std::string& name, const std::string& val)
{
	this->Lock(VarMap);
	std::map<const std::string, Variable*>::iterator it = this->m_variablesMap.find(name);

	if (it != this->m_variablesMap.end())
	{
		this->m_variablesMap[name]->Set(val);
		this->Unlock(VarMap);
		return (*it).second;
	}
	else
	{
		Variable* tmp = new Variable(name, val);
		this->m_variablesMap[name] = tmp;
		this->Unlock(VarMap);
		return tmp;
	}
}

/*--------------------------------------------------------------------------*/

Variable* CShell::AddVar(const std::string& name, float val)
{
	this->Lock(VarMap);
	std::map<const std::string, Variable*>::iterator it = this->m_variablesMap.find(name);

	if (it != this->m_variablesMap.end())
	{
		this->m_variablesMap[name]->Set(val);
		this->Unlock(VarMap);
		return (*it).second;
	}
	else
	{
		Variable* tmp = new Variable(name, val);
		this->m_variablesMap[name] = tmp;
		this->Unlock(VarMap);
		return tmp;
	}
}

/*--------------------------------------------------------------------------*/

std::string CShell::ListAllVars()
{
	std::stringstream ss;

	this->Lock(VarMap);

	for (std::map<const std::string, Variable*>::iterator Iter = this->m_variablesMap.begin(); Iter != this->m_variablesMap.end(); ++Iter)
	{
		ss << (*Iter).first << " = " << (*Iter).second->GetString() << "\n";
	}

	this->Unlock(VarMap);

	return ss.str();
}

/*--------------------------------------------------------------------------*/

void CShell::OnCalc(uint arg)
{
	this->Lock(CmdQueue);

	while (!this->m_commandsQueue.empty())
	{
		this->ParseCommand(this->m_commandsQueue.front());
		this->m_commandsQueue.pop();
	}

	this->Unlock(CmdQueue);
}

/*--------------------------------------------------------------------------*/

void CShell::ParseCommand(const std::string& cmd)
{
	if (cmd[0] == '!')
	{
		std::stringstream ss;
		std::string scmd = cmd.substr(1, cmd.size() - 1);
		ss << scmd;

		std::string sname;
		ss >> sname;
		Variable* var = this->GetVar(sname);

		if (var)
		{
			std::string svalue;
			ss >> svalue;
			var->Set(svalue);
			std::cout << "#g" << sname << " = " << svalue << std::endl;
		}
		else
		{
			std::cout << "#rVariable " << sname << " not found." << std::endl;
		}
	}
	else if (cmd[0] == '@')
	{
		std::stringstream ss;
		std::string scmd = cmd.substr(1, cmd.size() - 1);
		ss << scmd;

		std::string sname;
		ss >> sname;
		Variable* var = this->GetVar(sname);

		if (var)
		{
			std::cout << "#g" << sname << " = " << var->GetString() << std::endl;
		}
		else
		{
			std::cout << "#rVariable " << sname << " not found." << std::endl;
		}
	}
	else
	{
		AssertLogE(luaL_dostring(this->m_luaState, cmd.c_str()), "LUA: %s\n", lua_tostring(this->m_luaState, -1));
	}
}

/*--------------------------------------------------------------------------*/

lua_State* CShell::GetLuaState() const
{
	return this->m_luaState;
}

/*--------------------------------------------------------------------------*/

Variable* CShell::GetOrAddVar(const std::string& name)
{
	this->Lock(VarMap);
	std::map<const std::string, Variable*>::iterator it = this->m_variablesMap.find(name);

	if (it == this->m_variablesMap.end())
	{
		this->Unlock(VarMap);
		return this->AddVar("name", 0);
	}
	else
	{
		this->Unlock(VarMap);
		return (*it).second;
	}
}

/*--------------------------------------------------------------------------*/

void sh::exit()
{
	ViaMain::StopGame();
}

/*--------------------------------------------------------------------------*/

void sh::logi(const std::string& message)
{
	AssertLogI(true, message.c_str());
}

/*--------------------------------------------------------------------------*/

void sh::logw(const std::string& message)
{
	AssertLogW(true, message.c_str());
}

/*--------------------------------------------------------------------------*/

void sh::loge(const std::string& message)
{
	AssertLogE(true, message.c_str());
}

/*--------------------------------------------------------------------------*/

void sh::logc(const std::string& message)
{
	AssertLogC(true, message.c_str());
}

/*--------------------------------------------------------------------------*/

void sh::loadscript(const std::string& message)
{
	g_Shell->LoadScript(message);
}

/*--------------------------------------------------------------------------*/

void sh::setstring(const std::string& name, const std::string& value)
{
	Variable* v = g_Shell->GetVar(name);

	if (v == NULL)
	{
		v = g_Shell->AddVar(name, value);
	}
	else
	{
		v->Set(value);
	}
}

/*--------------------------------------------------------------------------*/

void sh::setint(const std::string& name, int value)
{
	Variable* v = g_Shell->GetVar(name);

	if (v == NULL)
	{
		v = g_Shell->AddVar(name, value);
	}
	else
	{
		v->Set(value);
	}
}

/*--------------------------------------------------------------------------*/

void sh::setfloat(const std::string& name, float value)
{
	Variable* v = g_Shell->GetVar(name);

	if (v == NULL)
	{
		v = g_Shell->AddVar(name, value);
	}
	else
	{
		v->Set(value);
	}
}

/*--------------------------------------------------------------------------*/

void sh::setbool(const std::string& name, bool value)
{
	Variable* v = g_Shell->GetVar(name);

	if (v == NULL)
	{
		v = g_Shell->AddVar(name, value);
	}
	else
	{
		v->Set(value);
	}
}

/*--------------------------------------------------------------------------*/

const std::string& sh::getstring(const std::string& name)
{
	return g_Shell->GetVar(name)->GetString();
}

/*--------------------------------------------------------------------------*/

int sh::getint(const std::string& name)
{
	return g_Shell->GetVar(name)->GetInt();
}

/*--------------------------------------------------------------------------*/

float sh::getfloat(const std::string& name)
{
	return g_Shell->GetVar(name)->GetFloat();
}

/*--------------------------------------------------------------------------*/

bool sh::getbool(const std::string& name)
{
	return g_Shell->GetVar(name)->GetBool();
}

/*--------------------------------------------------------------------------*/

float sh::randfloat(float a, float b)
{
	return ::RandFloat(a, b);
}

/*--------------------------------------------------------------------------*/

int sh::randint(int a, int b)
{
	return ::RandInt(a, b);
}

/*--------------------------------------------------------------------------*/

void sh::benchmark()
{
	ViaMain::StartBenchmark();
}

/*--------------------------------------------------------------------------*/